﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.SharePoint.WebControls;
using Fujitsu.Vetuma.Toolkit;
using Microsoft.SharePoint;
using VetumaForSharePoint.Resources;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using VetumaForSharePoint.Configuration;
using System.Collections.ObjectModel;
using System.Web.Security;
using Microsoft.SharePoint.Utilities;
using VetumaForSharePoint.DAL;
using Microsoft.SharePoint.Administration;
using System.Diagnostics;

namespace VetumaForSharePoint
{
    public class VetumaRegister : LayoutsPageBase
    {
        // Define controls used in aspx page.
        protected EncodedLiteral EncodedLiteralHeaderTitle;
        protected EncodedLiteral EncodedLiteralTitle;

        protected Label lblInfo;
        protected HyperLink HyperLinkToSite;

        protected HtmlTable TableRegister;

        protected Button ButtonRegister;

        protected Label LabelFirstName;
        protected Label LabelLastName;
        protected Label LabelSocialSecurityNumber;
        protected TextBox TextBoxEmail;
        // These validators validates email is in correct form.
        protected RequiredFieldValidator RequiredFieldValidator4; // Validates TextBoxEmail
        protected RegularExpressionValidator RegularExpressionValidator1; // Validates TextBoxEmail

        protected TextBox TextBoxUserName;
        protected RequiredFieldValidator RequiredFieldValidator1; // Validates TextBoxUserName

        protected TextBox TextBoxPassword;
        // These validators validates that password is in correct form. 
        protected RequiredFieldValidator RequiredFieldValidator2; // Validates TextBoxPassword
        protected RegularExpressionValidator RegularExpressionValidator2; // Validates TextBoxPassword

        protected TextBox TextBoxPasswordRetype;
        protected RequiredFieldValidator RequiredFieldValidator3; // Validates TextBoxPasswordRetype
        protected CompareValidator CompareValidator1; // Validates TextBoxPasswordRetype text to be the same than TextBoxPassword

        protected HtmlTable TableRecovery;

        protected Label LabelUserNameRecovery;
        protected Label LabelPasswordRecovery;

        protected HtmlTable TableEmail;

        // These controls are used when user wants to login with Vetuma and has not got user account.
        // Account is created on the fly, but email cannot be resolved without asking user.
        protected TextBox TextBoxEmail2;
        protected RequiredFieldValidator RequiredFieldValidator5; // Validates TextBoxEmail2
        protected RegularExpressionValidator RegularExpressionValidator3; // Validates TextBoxEmail2
        protected Button ButtonSaveEmail;


        // Allow anonymous access to this page
        protected override bool AllowAnonymousAccess
        {
            get
            {
                return true;
            }
        }

        // No need to require default layouts rights because of anonymous access
        protected override bool RequireDefaultLayoutsRights
        {
            get
            {
                return false;
            }
        }

        // No rights required to access this page
        protected override SPBasePermissions RightsRequired
        {
            get
            {
                return SPBasePermissions.EmptyMask;
            }
        }

        /// <summary>
        /// Overridden OnLoad
        /// </summary>
        /// <param name="e"></param>
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            try
            {
                if (!Page.IsPostBack)
                {
                    // Check where request comes to know how to act.
                    checkRequest();
                }
            }
            catch (Exception ex)
            {
                hideRegistrationForm();
                TableRecovery.Visible = false;
                lblInfo.Text = VetumaForSP.ResourceManager.GetString("Error");
                DataAccess.logError(ex, EventLogEntryType.Error);
            }
        }

        /// <summary>
        /// Register button Click-event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void ButtonRegister_Click(object sender, EventArgs e)
        {
            try
            {
                // Check is page valid
                if (Page.IsValid)
                {
                    // Register user
                    registerUser(false);
                }
            }
            catch (Exception ex)
            {
                hideRegistrationForm();
                TableRecovery.Visible = false;
                lblInfo.Text = VetumaForSP.ResourceManager.GetString("Error");
                DataAccess.logError(ex, EventLogEntryType.Error);
            }
        }

        /// <summary>
        /// Save email button Click-event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void ButtonSaveEmail_Click(object sender, EventArgs e)
        {
            try
            {
                // Check is page valid
                if (Page.IsValid)
                {
                    // Register user
                    registerUser(true);
                }
            }
            catch (Exception ex)
            {
                hideRegistrationForm();
                TableRecovery.Visible = false;
                lblInfo.Text = VetumaForSP.ResourceManager.GetString("Error");
                DataAccess.logError(ex, EventLogEntryType.Error);
            }
        }

        /// <summary>
        /// Registers user either on AD or Sql based on configuration in web.config
        /// </summary>
        private void registerUser(bool loginRegister)
        {
            // Get data from registration form
            string userName = TextBoxUserName.Text;
            string password = TextBoxPassword.Text;
            string email = TextBoxEmail.Text;
            string socialSecurityNumber = LabelSocialSecurityNumber.Text;
            string firstName = LabelFirstName.Text;
            string lastName = LabelLastName.Text;

            // Get current provider from membership providers.
            MembershipProvider currentProvider = Membership.Providers[VetumaForSharePointConfig.getConfig().RegistrationConfig.Provider];

            // User wants to login with Vetuma and does not already have an account.
            // Email is asked from user and all other data is generated automatically.
            if (loginRegister)
            {
                string lowerFirstName = firstName.ToLower().Trim(new char[] { '/', '-', ' ' });
                string lowerLastName = lastName.ToLower().Trim(new char[] { '/', '-', ' ' });

                lowerFirstName = replaceInvalidChars(lowerFirstName);
                lowerLastName = replaceInvalidChars(lowerLastName);

                email = TextBoxEmail2.Text;
                // Generate user name from first name and last name.
                userName = lowerFirstName + "." + lowerLastName;

                int ii = 1;

                // Test user name so long that no corresponding name found.
                while (currentProvider.GetUser(userName, false) != null)
                {
                    userName = lowerFirstName + "." + lowerLastName + ii.ToString();
                    ii++;
                };
                // Generate password which length is 7 chars and it contains 1 non alphanumeric char.
                password = Membership.GeneratePassword(7, 1);

                Random rand = new Random();

                // Add random number between 0 and 9 at the end of password to ensure that password is secure enough.
                password += rand.Next(0, 10);
            }

            bool accountCreated = false;

            try
            {
                // Create account
                accountCreated = DataAccess.createAccount(userName, password, email, socialSecurityNumber);
            }
            catch (VFSException vfsEx)
            {
                // Custom exception is thrown
                lblInfo.Text = vfsEx.Message;
                return;
            }
            catch (Exception ex)
            {
                lblInfo.Text = VetumaForSP.ResourceManager.GetString("Error");
                DataAccess.logError(ex, EventLogEntryType.Error);
                return;
            }

            // If account creation succeeded, add user to sharepoint groups
            if (accountCreated)
            {
                // Login name is build up from membership provider name and user name
                string loginName = currentProvider.Name + ":" + userName;

                bool ok = false;

                try
                {
                    string name = firstName + " " + lastName;

                    // Add user to sharepoint group(s) which are specified in web.config
                    ok = DataAccess.addUserToSharePointGroups(loginName, name, email);
                    if (ok)
                    {
                        // Login user and redirect if user has wanted to login with Vetuma.
                        if (loginRegister)
                        {
                            loginUserAndRedirect(currentProvider.GetUser(userName, false), password, !loginRegister);
                        }
                        // Account has been created
                        lblInfo.Text = VetumaForSP.ResourceManager.GetString("AccountCreated");
                        // Set navigate url to the hyperlink that redirects user to site and set hyperlink visible.

                        HyperLinkToSite.NavigateUrl = getRedirectUrl(userName);
                        HyperLinkToSite.Visible = true;
                    }
                    else
                    {
                        // Account creation failed
                        lblInfo.Text = VetumaForSP.ResourceManager.GetString("AccountCreatedGroupFailed");
                    }
                }
                catch (Exception ex)
                {
                    lblInfo.Text = VetumaForSP.ResourceManager.GetString("Error");
                    DataAccess.logError(ex, EventLogEntryType.Error);
                    return;
                }
                if (ok)
                {
                    hideRegistrationForm();
                }
            }
            else
            {
                lblInfo.Text = VetumaForSP.ResourceManager.GetString("CreateAccountFailed");
            }
        }

        /// <summary>
        /// Removes non a-z chars from string.
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private string replaceInvalidChars(string str)
        {
            str = str.Replace('ä', 'a');
            str = str.Replace('å', 'a');
            str = str.Replace('á', 'a');
            str = str.Replace('à', 'a');
            str = str.Replace('ö', 'o');
            str = str.Replace('ó', 'o');
            str = str.Replace('ò', 'o');
            str = str.Replace('ü', 'u');
            str = str.Replace('û', 'u');
            str = str.Replace('ú', 'u');
            str = str.Replace('ù', 'u');
            str = str.Replace('é', 'e');
            str = str.Replace('è', 'e');
            str = str.Replace('í', 'i');
            str = str.Replace('ì', 'i');

            return str;
        }

        /// <summary>
        /// Checks request.
        /// </summary>
        private void checkRequest()
        {
            // Check where request comes.
            // Request comes from registration link.
            if (Request["auth"] != null)
            {
                int auth = 0;
                int.TryParse(Request["auth"].ToString(), out auth);

                // Request comes from login page where user has clicked 'Register'
                if (auth == 1)
                {
                    // Redirect to Vetuma
                    authenticateUser();
                }
                else
                {
                    lblInfo.Text = VetumaForSP.ResourceManager.GetString("InvalidRequest");
                }
            }
            // Request comes from VETUMA
            else if (Request["vetumaStatus"] != null && Request["VetumaAction"] != null)
            {
                int vetumaStatus = 0;

                // Get vetumaStatus from connection string.
                int.TryParse(Request["vetumaStatus"].ToString(), out vetumaStatus);

                if (vetumaStatus > 0)
                {
                    // Handle response from Vetuma.
                    handleVetumaResponse(vetumaStatus);
                }
                else
                {
                    lblInfo.Text = VetumaForSP.ResourceManager.GetString("InvalidRequest");
                }
            }
            else
            {
                throw new InvalidOperationException("Invalid request, parameters missing: " + Request.Url.ToString());
            }
        }

        /// <summary>
        /// Handle vetuma response
        /// </summary>
        /// <param name="vetumaStatus">Parameter from request query string.</param>
        private void handleVetumaResponse(int vetumaStatus)
        {
            // Create response object.
            VetumaAuthenticationResponse response =
                new VetumaAuthenticationResponse(VetumaForSharePointConfig.getConfig().VetumaConfig.SharedSecretId,
                                                 VetumaForSharePointConfig.getConfig().VetumaConfig.SharedSecret);

            // Validate vetuma response.
            bool validate = response.Validate();

            if (validate)
            {
                switch (vetumaStatus)
                {
                    case 1:
                        // Authentication succeeded
                        // Check does user exist.
                        MembershipUser user = DataAccess.findUser(response.PersonId);
                        if (user == null)
                        {
                            if (Request["loginMethod"] != null)
                            {
                                // User wants to login with vetuma.
                                string vetumaLogin = VetumaForSP.ResourceManager.GetString("VetumaLogin");
                                EncodedLiteralTitle.Text = vetumaLogin;
                                EncodedLiteralHeaderTitle.Text = vetumaLogin;
                                hideRegistrationForm();
                                TableEmail.Visible = true;
                                // Set Vetuma info to controls even controls are not shown to user.
                                // Reason: Easier to handle after user has entered email address.
                                setVetumaInfoToControls(response);
                            }
                            else
                            {
                                // User does not have existing account.
                                showRegistrationForm();
                                setVetumaInfoToControls(response);
                            }
                        }
                        else
                        {
                            // User already has an account.
                            // Check does user want to login with Vetuma
                            if (Request["loginMethod"] != null)
                            {
                                loginUserAndRedirect(user, "", false);
                            }
                            else
                            {
                                // User is trying to re-register. This is not allowed so let's show user name and password to user.
                                hideRegistrationForm();
                                TableRecovery.Visible = true;
                                // Show user's user name.
                                LabelUserNameRecovery.Text = user.UserName;
                                // Recover user's password and show it to user.
                                LabelPasswordRecovery.Text = getUsersPassword(user);
                            }
                        }
                        break;
                    case 2:
                        // Authentication cancelled by user
                        hideRegistrationForm();
                        lblInfo.Text = VetumaForSP.ResourceManager.GetString("AuthenticationCancelled");
                        break;
                    case 3:
                        // Authentication error
                        hideRegistrationForm();
                        lblInfo.Text = VetumaForSP.ResourceManager.GetString("AuthenticationError");
                        break;
                    default:
                        break;
                }

            }
            else
            {
                throw new ApplicationException("Vetuma response was not valid.");
            }
        }

        /// <summary>
        /// Logs user in and redirects to site.
        /// </summary>
        /// <param name="user">The user to login.</param>
        private void loginUserAndRedirect(MembershipUser user, string usersPassword, bool loginOnly)
        {
            if (string.IsNullOrEmpty(usersPassword))
            {
                usersPassword = getUsersPassword(user);
            }

            // Get current membership provider and validate user.
            if (Membership.Providers[VetumaForSharePointConfig.getConfig().RegistrationConfig.Provider].ValidateUser(user.UserName, usersPassword))
            {
                FormsAuthentication.SetAuthCookie(user.UserName, false);

                if (!loginOnly)
                {
                    string url = getRedirectUrl(user.UserName);

                    try
                    {
                        SPUtility.Redirect(url, SPRedirectFlags.DoNotEncodeUrl, HttpContext.Current);
                    }
                    catch (System.Threading.ThreadAbortException)
                    {
                        url = "";
                    }
                }
            }

        }

        /// <summary>
        /// Gets redirect url either from Forms Authentication object or from current SPWeb.
        /// </summary>
        /// <param name="userName">Current user's user name.</param>
        /// <returns>Url where to redirect user.</returns>
        private string getRedirectUrl(string userName)
        {
            if (!string.IsNullOrEmpty(FormsAuthentication.GetRedirectUrl(userName, false)))
            {
                return FormsAuthentication.GetRedirectUrl(userName, false);
            }
            else
            {
                return SPContext.Current.Web.Url;
            }
        }

        /// <summary>
        /// Finds user's password.
        /// </summary>
        /// <param name="user">The user whose password needs to be found.</param>
        /// <returns>User's password.</returns>
        private string getUsersPassword(MembershipUser user)
        {
            VetumaForSharePointConfig config = VetumaForSharePointConfig.getConfig();

            string userStore = config.RegistrationConfig.UserStore;

            if (userStore.ToLower() == "sql")
            {
                return user.GetPassword(DataAccess.securityQuestionAnswer);
            }
            else if (userStore.ToLower() == "ad")
            {
                VetumaUser vUser = DataAccess.findVetumaUserByUserName(user.UserName);

                return VFSHasher.Decrypt(vUser.HashedPassword, config.RegistrationConfig.VFSKey);
            }
            else
            {
                throw new NotSupportedException("Specified user store is not supported: " + userStore);
            }
        }

        /// <summary>
        /// Sets information that came from Vetuma to corresponding controls in UI.
        /// </summary>
        /// <param name="response">Response from Vetuma</param>
        private void setVetumaInfoToControls(VetumaAuthenticationResponse response)
        {
            LabelFirstName.Text = response.FirstName;
            LabelLastName.Text = response.LastName;
            LabelSocialSecurityNumber.Text = response.PersonId;
        }

        /// <summary>
        /// Hides tables that contains registration controls
        /// </summary>
        private void hideRegistrationForm()
        {
            TableRegister.Visible = false;
        }

        /// <summary>
        /// Shows tables that contains registration controls and loads data from Vetuma to controls
        /// </summary>
        private void showRegistrationForm()
        {
            TableRegister.Visible = true;
        }

        /// <summary>
        /// Redirects user to Vetuma
        /// </summary>
        private void authenticateUser()
        {
            // Authentication in Vetuma can be successfull, it can be cancelled by user or error can occur
            // Define urls for these different scenarios
            string url = "https://" + Request.Url.Host;

            if (Request.Url.Port != 80)
            {
                url += ":" + Request.Url.Port.ToString();
            }
            url += Request.Url.LocalPath;

            string successUrl = url + "?vetumaStatus=1&VetumaAction=1";
            string cancelUrl = url + "?vetumaStatus=2&VetumaAction=1";
            string errorUrl = url + "?vetumaStatus=3&VetumaAction=1";

            // Check does user want to login with Vetuma
            if (Request["loginMethod"] != null)
            {
                successUrl += "&loginMethod=1";
            }

            Collection<VetumaLoginMethod> methods = getMethods();

            VetumaAuthenticationRequest request = new VetumaAuthenticationRequest(
                    VetumaForSP.ResourceManager.GetString("Next"),
                    VetumaForSP.ResourceManager.GetString("NoJavascript"),
                    "fi",
                    methods,
                    new Uri(successUrl), // Success
                    new Uri(cancelUrl), // Cancel
                    new Uri(errorUrl), // Error
                    new Uri(VetumaForSharePointConfig.getConfig().VetumaConfig.PostUrl),
                    VetumaForSharePointConfig.getConfig().VetumaConfig.SharedSecretId,
                    VetumaForSharePointConfig.getConfig().VetumaConfig.SharedSecret,
                    VetumaForSharePointConfig.getConfig().VetumaConfig.ApplicationId,
                    VetumaForSharePointConfig.getConfig().VetumaConfig.ConfigurationId);

            //request.ExtraData = "VTJ1";
            //request.ExtraData = "VTJTT=VTJ-VETUMA-Perus";

            // Submit the authentication request
            try
            {
                request.Submit();
            }
            catch (System.Threading.ThreadAbortException)
            {
                url = "";
                // This error is caused by redirection in sharepoint. No need to handle.
            }
        }

        /// <summary>
        /// Defines methods that Vetuma should display for user. Specified in web.config.
        /// </summary>
        /// <returns>Collection of VetumaLoginMethod</returns>
        internal static Collection<VetumaLoginMethod> getMethods()
        {
            Collection<VetumaLoginMethod> methods = new Collection<VetumaLoginMethod>();

            string methodsString = VetumaForSharePointConfig.getConfig().VetumaConfig.AuthenticationMethods;

            if (methodsString == "" || methodsString == null)
            {
                methods.Add(VetumaLoginMethod.None);
                return methods;
            }

            if (methodsString.Contains("tupas"))
                methods.Add(VetumaLoginMethod.Tupas);
            if (methodsString.Contains("mobile"))
                methods.Add(VetumaLoginMethod.Mobile);
            if (methodsString.Contains("hst"))
                methods.Add(VetumaLoginMethod.HST);
            if (methodsString.Contains("phonenumber"))
                methods.Add(VetumaLoginMethod.PhoneNumber);
            if (methodsString.Contains("username"))
                methods.Add(VetumaLoginMethod.UserName);

            return methods;
        }

    }
}
